/**
 * 1,2,3 级索引 为动态生成
 * 1级索引 为 1,2,3
 * 2级索引 为 4,5,6
 * 3级索引 为 7,8,9
 * /

/**
 * 2级棋子 => 2 个 1级棋子 合成
 * 3级棋子 => 3 个 1级棋子 合成
 */

/**
 * 优先匹配高级棋子
 * 检测所有的高级棋子的位置 匹配目标
 */

/**
 * 线路生成规则:
 * 1. 一字型
 * 2. T型
 * 3. 十字型
 */

const MatchStatus = {
    GAME_OVER: 2,   // 棋盘棋子已满 && 没有匹配路线
    NO_MATCH: 1,    // 没有匹配 路线
    OK: 0,          // 匹配成功
}

let MR = {
    
    // 最大基础棋子个数
    maxItemNum: 5,
    
    // 棋子最大等级
    maxItemLv: 3,

    // 目标列最大个数
    maxTarItemNum: 3,

    // 这里只的是 9宫格
    // 棋子最大索引
    boradSize: {w: 3, h: 3},
    maxBoradNum: 9,

    // 缓存线路表
    _cacheLineList: {"2":{}, "3":{}},
    _cacheContainsLineList: [[],[],[],[],[],[],[],[],[],[]],

    init() {
        this.maxBoradNum = this.boradSize.w * this.boradSize.h;
        for(let i = 1; i <= this.maxBoradNum; i++) {
            this.getLineByIndex(i);
        };
    },

    // 获取棋子的编号
    getItemNumListByLv(lv) {
        let tmpList = [];
        if(lv > this.maxItemLv || lv <= 0) return tmpList;
        let startIndex = (lv-1) * this.maxItemNum + 1;
        let endIndex = lv * this.maxItemNum;
        for(let i = startIndex; i <= endIndex; i++) {
            tmpList.push(i);
        }
        return tmpList;
    },

    // 获取所有棋子的编号
    getItemNumListAll() {
        let startIndex = 1;
        let endIndex = this.maxItemLv * this.maxItemNum;
        let tmpList = [];
        for(let i = startIndex; i <= endIndex; i++) {
            tmpList.push(i);
        };
        return tmpList;
    },

    // 检查棋盘索引
    checkBoardIndex(index) {
        if(index <= 0 || index > this.maxBoradNum) return false;
        return true;
    },

    // 获取棋盘周围的索引
    getAroundIndexWithIndex(index) {
        let tmpList = [];
        if(!this.checkBoardIndex(index)) return tmpList;

        // 优先返回缓存里面的路线 不在从新计算
        if(this._cacheLineList["2"][index]) {
            return this._cacheLineList["2"][index];
        }

        let rIndex = index + 1, lIndex = index -1, tIndex = index - this.boradSize.w, bIndex = index + this.boradSize.w;
        if(this.checkBoardIndex(rIndex) && index % this.boradSize.w != 0) {
            tmpList.push(rIndex);
        }
        if(this.checkBoardIndex(lIndex) && index % this.boradSize.w != 1) {
            tmpList.push(lIndex);
        }
        if(this.checkBoardIndex(tIndex)) {
            tmpList.push(tIndex);
        }
        if(this.checkBoardIndex(bIndex)) {
            tmpList.push(bIndex);
        }
        if(!this._cacheLineList["2"][index]) {
            this._cacheLineList["2"][index] = tmpList;
        }
        return tmpList;
    },

    // 获取以 任意索引开头的 路线
    getLineByIndex(index) {
        // 适用于 线的长度为 3
        let outLineList = [];
        if(!this.checkBoardIndex(index)) return outLineList;

        // 优先返回缓存里面的路线 不在从新计算
        if(this._cacheLineList["3"][index]) {
            return this._cacheLineList["3"][index];
        }

        let tmpDataList = [[index]];
        let aIndexList = this.getAroundIndexWithIndex(index);
        tmpDataList.push(aIndexList);

        let eachLineData = [];
        aIndexList.forEach(val => {
            eachLineData.push(this.getAroundIndexWithIndex(val));
        });
        
        tmpDataList.push(eachLineData);

        let headIndex = index;

        let two = tmpDataList[1], three = tmpDataList[2];
        for (let i = 0; i < two.length; ++i) {
            let v_2 = two[i];
            for (let j = 0; j < three[i].length; ++j) {
                let v_3 = three[i][j];
                if (headIndex == v_3) continue;
                let arr = [headIndex, v_2, v_3];
                this._cacheContainsLineList[headIndex].push(arr);
                this._cacheContainsLineList[v_2].push(arr);
                this._cacheContainsLineList[v_3].push(arr);
                outLineList.push(arr);
            }
        }
        // 扩充加入对角的路线
        let extenLine = this.getDiagonalByIndex(index);
        if(extenLine.length == 3) {
            this._cacheContainsLineList[extenLine[0]].push(extenLine);
            this._cacheContainsLineList[extenLine[1]].push(extenLine);
            this._cacheContainsLineList[extenLine[2]].push(extenLine);
            outLineList.push(extenLine);
        }

        if(!this._cacheLineList["3"][index]) {
            this._cacheLineList["3"][index] = outLineList;
        }
        return outLineList;
    },

    // 获取对角线
    getDiagonalByIndex(index) {
        let outLine = [];

        if(!this.checkBoardIndex(index)) return outLine;

        // 屏蔽不是对角的索引
        let lt = 1;
        let lb = this.maxBoradNum-this.boradSize.w + 1;
        let rt = this.boradSize.w;
        let rb = this.maxBoradNum;
        let startIndex = -1;
        
        switch (index) {
            case lt:
                outLine.push(lt);
                startIndex = lt;
                for(let i = 1; i < this.boradSize.h; i++) {
                    startIndex += (this.boradSize.w+1);
                    outLine.push(startIndex);
                }
                break;
            case lb:
                outLine.push(lb);
                startIndex = lb;
                for(let i = 1; i < this.boradSize.h; i++) {
                    startIndex -= (this.boradSize.w-1);
                    outLine.push(startIndex);
                }
                break;
            case rt:
                outLine.push(rt);
                startIndex = rt;
                for(let i = 1; i < this.boradSize.h; i++) {
                    startIndex += (this.boradSize.w-1);
                    outLine.push(startIndex);
                }
                break;
            case rb:
                outLine.push(rb);
                startIndex = rb;
                for(let i = 1; i < this.boradSize.h; i++) {
                    startIndex -= (this.boradSize.w+1);
                    outLine.push(startIndex);
                }
                break;
        };
        
        return outLine;
    },

    match(tarList, boardList) {
        let result = {
            status: MatchStatus.OK, // 0 成功
            data: [],
            totalLineNum: 0,
        };
        if(!tarList || tarList.length <= 0 || !boardList || boardList.length <= 0) {
            result.status = MatchStatus.NO_MATCH;
            return result;
        };

        let f = true;

        tarList.forEach((line) => {
            let d = this.matchLine(line, boardList, result);

            result.data.push(d);

            f = f && d.lineNum <=0;
            result.totalLineNum += d.lineNum;
        });

        if(f) {
            let yNum = 0;
            boardList.forEach(val => {
                if(val != -1) {
                    yNum++;
                }
            });
            if(yNum >= this.maxBoradNum) {
                result.status = MatchStatus.GAME_OVER;
            } else {
                result.status = MatchStatus.NO_MATCH;
            }
        }

        return result;
    },

    matchLine(line, boardList, result) {
        if(!line || line.length < this.maxTarItemNum) {
            return {matchLines: [], lineNum: 0};
        };

        let newLine = this.adjustTarItemData(line);
        let matchLines = [];
        let lineNum = 0;
        for(let i = 1; i <= this.maxBoradNum; i++) {
            let lines = this.getLineByIndex(i);
            let boardNumLines = this.getBoardLines(lines, boardList);
            for(let j in boardNumLines) {
                let boardNumLine = boardNumLines[j];
                let line = lines[j];
                let outLines = [{},{},{}];
                if(this.matchRule(newLine, boardNumLine, boardList, line, outLines)) {
                    // 匹配
                    // let mul = 1;
                    // outLines.forEach(item => {
                    //     mul *= item.lines.length;
                    // });

                    // lineNum += mul;
                    let d_ = {
                        baseLine: line,
                        outLines: outLines,
                        totalLineNum: 0,
                    };

                    this.adjustOutLlinePlgin(d_);
                    lineNum += d_.outLines.length;
                    d_.totalLineNum = d_.outLines.length;

                    matchLines.push(d_);
                }
            }
        }
        return {matchLines: matchLines, lineNum: lineNum};
    },

    // 调整outliens
    adjustOutLlinePlgin(d) {
        if(d.outLines.length == 3) {
            let d_ = [];
            for(let i = 0; i < d.outLines[0].lines.length; i++) {
                for(let j = 0; j < d.outLines[1].lines.length; j++) {
                    for(let k = 0; k < d.outLines[2].lines.length; k++) {
                        let extLine = [
                            {line: d.outLines[0].lines[i], index: d.outLines[0].index, lv: d.outLines[0].lv},
                            {line: d.outLines[1].lines[j], index: d.outLines[1].index, lv: d.outLines[1].lv},
                            {line: d.outLines[2].lines[k], index: d.outLines[2].index, lv: d.outLines[2].lv},
                        ];
                        d_.push(extLine);
                    }
                }
            }
            d.outLines = d_;
        }
    },

    // 获取棋盘编号线
    getBoardLines(lines, boardList) {
        let outBoardNumLines = [];
        for(let i in lines) {
            let line = lines[i];
            let outLine = [];
            for(let j in line) {
                let v = line[j];
                if(!boardList[v] /*|| boardList[v] == -1*/) {
                    break;
                }
                outLine[j] = boardList[v];
            }
            outBoardNumLines.push(outLine);
        }
        return outBoardNumLines;
    },

    // 判断其他 2- 3 等级 元素 是否满足
    matchRule(newLine, boardNumLine, boardList, line, outLines) {
        let flag1 = this.matchBaseRule(newLine.itemBaseNumList, boardNumLine);
        if(newLine.maxItemLv == 1 && flag1) {

            boardNumLine.forEach((val, index) => {
                outLines[index] = {
                    lines: [[line[index]]],
                    index: line[index],
                    lv: 1,
                };
            })

            return true;
        } else if(!flag1) {
            return false;
        }

        for(let i in newLine.itemCurLvList) {
            if(newLine.itemCurLvList[i] == 1) {
                outLines[i] = {
                    lines: [[line[i]]],
                    index: line[i],
                    lv: 1,
                };
            } else if(newLine.itemCurLvList[i] == 2) {
                let newBoardList = this.copyArray(boardList);
                
                let lineCopy = this.copyArray(line);
                lineCopy[i]=0;
                this.setBoardVal(lineCopy, -1, newBoardList);
                let index = line[i];
                let num = boardNumLine[i];

                let tIndex = this.getAroundIndexWithIndex(index);
                let tLines = [];
                tIndex.forEach(val => {
                    tLines.push([index, val]);
                });

                let tBoardLines = this.getBoardLines(tLines, newBoardList)
                let outTLines = [];
                for(let j in tBoardLines) {
                    if(this.matchBaseRule(tBoardLines[j], [num, num])) {
                        outTLines.push(tLines[j]);
                    };
                }

                outLines[i] = {
                    lines: outTLines,
                    index: index,
                    lv: 2,
                };
            } else if(newLine.itemCurLvList[i] == 3) {
                let newBoardList = this.copyArray(boardList);
                let lineCopy = this.copyArray(line);
                lineCopy[i]=0;
                this.setBoardVal(lineCopy, -1, newBoardList);
                let index = line[i];
                let num = boardNumLine[i];

                let thLines = this._cacheContainsLineList[index];
                let thBoardLines = this.getBoardLines(thLines, newBoardList);

                let outThLines = [];
                for(let j in thBoardLines) {
                    if(this.matchBaseRule(thBoardLines[j], [num, num, num])) {
                        if(thLines[j][0] != line[0] && !this.array2ContainsArray(outThLines, thLines[j])) {
                            outThLines.push(thLines[j]);
                        }
                    };
                }

                outLines[i] = {
                    lines: outThLines,
                    index: index,
                    lv: 3,
                };
            }
        }

        let f = true;
        outLines.forEach(item => {
            f = f && (item.lines ? item.lines.length > 0 : false);
        })

        if(f) {
            return true
        } else {
            return false;
        }
    },

    //设置 board 值
    setBoardVal(indexList, val, boardList) {
        indexList.forEach(index => {
            boardList[index] = val;
        })
    },

    // 判断两条线是否相等
    matchBaseRule(line1, line2) {
        if(!line1 || line1.length <= 0 || !line2 || line2.length <= 0 || line1.length != line2.length) return false;
        for(let index in line1) {
            let val1 = line1[index];
            let val2 = line2[index];
            if(val1 != val2) {
                return false;
            }
        }

        return true;
    },

    // 调成目标列棋子线数据
    adjustTarItemData(line) {
        let result = {};
        if(!line || line.length <= 0) result;
        let itemCurLvList = [];
        let itemBaseNumList = [];
        let maxItemLv = 1;
        // 降级
        line.forEach((num, index) => {
            let curLv = Math.ceil(num / this.maxItemNum);
            let baseNum = num % this.maxItemNum;
            if(baseNum == 0) baseNum = this.maxItemNum;
            itemBaseNumList.push(baseNum);
            itemCurLvList.push(curLv);
            if(maxItemLv < curLv) {
                maxItemLv = curLv;
            }
        });
        result["itemCurLvList"] = itemCurLvList;
        result["itemBaseNumList"] = itemBaseNumList;
        result["maxItemLv"] = maxItemLv;
        return result;
    },

    // 拷贝数组
    copyArray(arr1) {
        let outArr = [];
        arr1.forEach(val => {
            outArr.push(val);
        })
        return outArr;
    },

    array2ContainsArray(arr2, arr1) {
        let copy_arr1 = this.copyArray( arr1 );
        for(let key in arr2) {
            let arr = arr2[key];
            let f1 = copy_arr1.toString();
            let f1_r = copy_arr1.reverse().toString();
            
            let f2 = arr.toString();
            if(f2 == f1 || f2 == f1_r) {
                return true;
            }
        }
        return false;
    },
};

module.exports = MR;

// console.log(JSON.stringify(MR.match([
//     [12,1,1],
// ], [-1,
//     2,  2, 2,
//     -1, 1, -1,
//     -1, 1, -1,
// ])));